#include "unity.h"
#include "test_sxnutils.h"
#include "sxnutils.h"
#include <string.h>
#include "stdio.h"

const char * cmds_104n[] = {
    // 104n commands
    "$AM,REQ,1*7B\r\n",
    "$AM,RQT,1*6A\r\n",
    "$AM,RUN,1*74\r\n",
    "$AM,STP,1*6A\r\n"
};
char checksum_104n[sizeof(cmds_104n) / sizeof(cmds_104n[0])];

const char * cmds_104t[] = {
    // 104t commands
    "$A9,REQ,1*0F\r\n",
    "$A9,RUN,1*00\r\n",
    "$A9,STP,1*1E\r\n"
};
char checksum_104t[sizeof(cmds_104t) / sizeof(cmds_104t[0])];


void getChecksums(void) { 
    uint64_t i;
    int c;
    for (i = 0; i < sizeof(cmds_104n) / sizeof(cmds_104n[0]); i++) {
        checksum_104n[i] = sscanf_s(&cmds_104n[i][10], "%02X", &c);
        checksum_104n[i] = (char)c;
        // printf("Checksum for cmd %d(%s): %02X\n", i, cmds_104n[i], checksum_104n[i]);
    }
    for (i = 0; i < sizeof(cmds_104t) / sizeof(cmds_104t[0]); i++) {
        checksum_104t[i] = sscanf_s(&cmds_104t[i][10], "%02X", &c);
        checksum_104t[i] = (char)c;
        // printf("Checksum for cmd %d(%s): %02X\n", i, cmds_104t[i], checksum_104t[i]);
    }
}

void setUp(void) {
}

void tearDown(void) {
}

void test_calc_checksum(void) {
    size_t i;
    char tmp[50];

    getChecksums();
    for (i = 0; i < sizeof(cmds_104n) / sizeof(cmds_104n[0]); i++) {
        sprintf(tmp, "%s", cmds_104n[i]+1);
        tmp[strlen(tmp) - 5] = '\0'; // Remove the checksum and
        TEST_ASSERT_EQUAL(checksum_104n[i], calc_checksum(tmp));
    }
    for (i = 0; i < sizeof(cmds_104t) / sizeof(cmds_104t[0]); i++) {
        sprintf(tmp, "%s", cmds_104t[i]+1);
        tmp[strlen(tmp) - 5] = '\0'; // Remove the checksum and
        TEST_ASSERT_EQUAL(checksum_104t[i], calc_checksum(tmp));
    }
}

void test_sxn_compose_packet(void) {
    size_t i;
    char tmp[50];
    char packet[50];

    for (i = 0; i < sizeof(cmds_104n) / sizeof(cmds_104n[0]); i++) {
        strncpy(tmp, cmds_104n[i] + 1, strlen(cmds_104n[i]) - 6);
        tmp[strlen(cmds_104n[i]) - 6] = '\0'; // Remove the checksum and CRLF
        sxn_compose_packet(tmp, packet, sizeof(packet));
        TEST_ASSERT_EQUAL_STRING(cmds_104n[i], packet);
    }
    for (i = 0; i < sizeof(cmds_104t) / sizeof(cmds_104t[0]); i++) {
        strncpy(tmp, cmds_104t[i] + 1, strlen(cmds_104t[i]) - 6);
        tmp[strlen(cmds_104t[i]) - 6] = '\0'; // Remove the checksum and CRLF
        sxn_compose_packet(tmp, packet, sizeof(packet));
        TEST_ASSERT_EQUAL_STRING(cmds_104t[i], packet);
    }
}

void test_get_packet_body(void) {
    size_t i;
    char dst[50];
    char tmp[50];
    for (i = 0; i < sizeof(cmds_104n) / sizeof(cmds_104n[0]); i++) {
        get_packet_body(cmds_104n[i], dst, sizeof(dst));
        strncpy(tmp, cmds_104n[i] + 1, strlen(cmds_104n[i]) - 6);
        tmp[strlen(cmds_104n[i]) - 6] = '\0';
        TEST_ASSERT_EQUAL_STRING(tmp, dst);
    }
    for (i = 0; i < sizeof(cmds_104t) / sizeof(cmds_104t[0]); i++) {
        get_packet_body(cmds_104t[i], dst, sizeof(dst));
        strncpy(tmp, cmds_104t[i] + 1, strlen(cmds_104t[i]) - 6);
        tmp[strlen(cmds_104t[i]) - 6] = '\0';
        TEST_ASSERT_EQUAL_STRING(tmp, dst);
    }
}

void test_validate_packet(void) {
    size_t i;
    for (i = 0; i < sizeof(cmds_104n) / sizeof(cmds_104n[0]); i++) {
        TEST_ASSERT_EQUAL(1, validate_packet(cmds_104n[i]));
    }
    for (i = 0; i < sizeof(cmds_104t) / sizeof(cmds_104t[0]); i++) {
        TEST_ASSERT_EQUAL(1, validate_packet(cmds_104t[i]));
    }
    // Test invalid packets
    TEST_ASSERT_EQUAL(0, validate_packet("$A9TEST*15\r\n")); // Invalid checksum
    TEST_ASSERT_EQUAL(0, validate_packet("$A9TEST*15")); // Missing CRLF
}

void test_sxn_conv_cmd_104t_104n(void) {
    size_t i;
    char tmp[50];
    char packet_n[50];
    
    for (i = 0; i < sizeof(cmds_104t) / sizeof(cmds_104t[0]); i++) {
        TEST_ASSERT_EQUAL(1, sxn_conv_cmd_104t_104n(cmds_104t[i], packet_n));
        // Check the converted packet
        strncpy(tmp, packet_n, 3);
        tmp[3] = '\0';
        TEST_ASSERT_EQUAL_STRING("$AM", tmp);
    }
}

void test_sxn_conv_cmd_104t_154nh(void) {
    size_t i;
    char tmp[50];
    char packet_n[50];
    
    for (i = 0; i < sizeof(cmds_104t) / sizeof(cmds_104t[0]); i++) {
        TEST_ASSERT_EQUAL(1, sxn_conv_cmd_104t_154nh(cmds_104t[i], packet_n));
        // Check the converted packet
        strncpy(tmp, packet_n, 3);
        tmp[3] = '\0';
        TEST_ASSERT_EQUAL_STRING("$Et", tmp);
    }
}

void test_sxn_conv_cmd_104n_154nh(void) {
    size_t i;
    char tmp[50];
    char packet_n[50];
    
    for (i = 0; i < sizeof(cmds_104n) / sizeof(cmds_104n[0]); i++) {
        TEST_ASSERT_EQUAL(1, sxn_conv_cmd_104n_154nh(cmds_104n[i], packet_n));
        // Check the converted packet
        strncpy(tmp, packet_n, 3);
        tmp[3] = '\0';
        TEST_ASSERT_EQUAL_STRING("$Et", tmp);
    }
}

const char *rsp_req_154nhs_bodies[] = {
    "Et,1,1,0,0,12345,0,0,0,0,1",  // Single head
    "Et,1,1,0,0,12345,1,0,0,23456,0,0,0,0,1",  // Two heads
    "Et,1,1,0,0,12345,1,0,0,23456,1,0,0,34567,0,0,0,0,1",  // Three heads
    "Et,1,1,0,0,12345,1,0,0,23456,1,0,0,34567,1,0,0,45678,0,0,0,0,1",  // Four heads
};
const char *rsp_req_104n_bodies[] = {
    "AM,1,1,0,0,12345,0,0,0,00000,0,0,0,00000,0,0,0,00000,0,0,0,0,1",  // Single head
    "AM,1,1,0,0,12345,1,0,0,23456,0,0,0,00000,0,0,0,00000,0,0,0,0,1",  // Two heads
    "AM,1,1,0,0,12345,1,0,0,23456,1,0,0,34567,0,0,0,00000,0,0,0,0,1",  // Three heads
    "AM,1,1,0,0,12345,1,0,0,23456,1,0,0,34567,1,0,0,45678,0,0,0,0,1"  // Four heads
};

void test_sxn_conv_rsp_req_154nh_104n(void) {
	size_t i;
	int checksum;
	char tmp1[MAX_SXN_RESP_PACKET_LEN + 1];
	char tmp2[MAX_SXN_RESP_PACKET_LEN + 1];
	char packet_n[MAX_SXN_RESP_PACKET_LEN + 1];

	for (i = 0; i < sizeof(rsp_req_154nhs_bodies) / sizeof(rsp_req_154nhs_bodies[0]); i++) {
		checksum = calc_checksum(rsp_req_154nhs_bodies[i]);
		sprintf_s(tmp1, sizeof(tmp1), "$%s*%.2X\r\n", rsp_req_154nhs_bodies[i], checksum);
		sxn_conv_rsp_req_154nh_104n(tmp1, packet_n);
		TEST_ASSERT_EQUAL(1, sxn_conv_rsp_req_154nh_104n(tmp1, packet_n));
		// printf("orig: %s, converted: %s\n", tmp1, packet_n);
		checksum = calc_checksum(rsp_req_104n_bodies[i]);
		sprintf_s(tmp2, sizeof(tmp2), "$%s*%.2X\r\n", rsp_req_104n_bodies[i], checksum);
		// printf("dest: %s\n", tmp2);
		TEST_ASSERT_EQUAL_STRING(tmp2, packet_n);
	}
}
